package com.shm.leetcode;

import java.util.*;

/**
 * @author: shm
 * @dateTime: 2020/11/17 10:38
 * @description: 1030. 距离顺序排列矩阵单元格
 * 给出 R 行 C 列的矩阵，其中的单元格的整数坐标为 (r, c)，满足 0 <= r < R 且 0 <= c < C。
 *
 * 另外，我们在该矩阵中给出了一个坐标为 (r0, c0) 的单元格。
 *
 * 返回矩阵中的所有单元格的坐标，并按到 (r0, c0) 的距离从最小到最大的顺序排，其中，两单元格(r1, c1) 和 (r2, c2) 之间的距离是曼哈顿距离，|r1 - r2| + |c1 - c2|。（你可以按任何满足此条件的顺序返回答案。）
 *
 *
 *
 * 示例 1：
 *
 * 输入：R = 1, C = 2, r0 = 0, c0 = 0
 * 输出：[[0,0],[0,1]]
 * 解释：从 (r0, c0) 到其他单元格的距离为：[0,1]
 * 示例 2：
 *
 * 输入：R = 2, C = 2, r0 = 0, c0 = 1
 * 输出：[[0,1],[0,0],[1,1],[1,0]]
 * 解释：从 (r0, c0) 到其他单元格的距离为：[0,1,1,2]
 * [[0,1],[1,1],[0,0],[1,0]] 也会被视作正确答案。
 * 示例 3：
 *
 * 输入：R = 2, C = 3, r0 = 1, c0 = 2
 * 输出：[[1,2],[0,2],[1,1],[0,1],[1,0],[0,0]]
 * 解释：从 (r0, c0) 到其他单元格的距离为：[0,1,1,2,2,3]
 * 其他满足题目要求的答案也会被视为正确，例如 [[1,2],[1,1],[0,2],[1,0],[0,1],[0,0]]。
 *
 *
 * 提示：
 *
 * 1 <= R <= 100
 * 1 <= C <= 100
 * 0 <= r0 < R
 * 0 <= c0 < C
 */
public class AllCellsDistOrder {

    /**
     * @author: shm
     * @dateTime: 2020/11/17 12:01
     * @description: 方法一：直接排序
     * 思路及解法
     * 复杂度分析
     *
     * 时间复杂度：O(RC \log(RC))O(RClog(RC))，存储所有点时间复杂度 O(RC)O(RC)，排序时间复杂度 O(RC \log(RC))O(RClog(RC))。
     *
     * 空间复杂度：O(\log(RC))O(log(RC))，即为排序需要使用的栈空间，不考虑返回值的空间占用。
     *
     * 最容易想到的方法是首先存储矩阵内所有的点，然后将其按照哈曼顿距离直接排序。
     */
    public int[][] allCellsDistOrder(int R, int C, int r0, int c0) {
        List<int[]> ans = new ArrayList<>();
        for(int i=0;i<R;i++){
            for(int j=0;j<C;j++){
                ans.add(new int[]{i,j});
            }
        }
        Collections.sort(ans, new Comparator<int[]>() {
            @Override
            public int compare(int[] x, int[] y) {
                return Math.abs(x[0]-r0)+Math.abs(x[1]-c0)-Math.abs(y[0]-r0)-Math.abs(y[1]-c0);
            }
        });
        return ans.toArray(new int[1][R*C]);
    }

    /**
     * @author: shm
     * @dateTime: 2020/11/17 12:01
     * @description: 方法二：桶排序
     * 思路及解法
     * 复杂度分析
     *
     * 时间复杂度：O(RC)O(RC)，存储所有点时间复杂度 O(RC)O(RC)，桶排序时间复杂度 O(RC)O(RC)。
     * 空间复杂度：O(RC)O(RC)，需要存储矩阵内所有点。
     *
     * 注意到方法一中排序的时间复杂度太高。实际在枚举所有点时，我们可以直接按照哈曼顿距离分桶。这样我们就可以实现线性的桶排序。
     */
    public int[][] allCellsDistOrder_2(int R, int C, int r0, int c0) {
        int maxDist = Math.max(r0,R-1-r0)+Math.max(c0,C-1-c0);
        List<List<int[]>> buckets = new ArrayList<>();
        for (int i = 0; i <= maxDist; i++) {
            buckets.add(new ArrayList<>());
        }

        for (int i = 0; i < R; i++) {
            for (int j = 0; j < C; j++) {
                buckets.get(Math.abs(i-r0)+Math.abs(j-c0)).add(new int[]{i,j});
            }
        }
        List<int[]> ans = new ArrayList<>();
        for (List<int[]> bucket : buckets) {
            for (int[] ints : bucket) {
                if (ints!=null) {
                    ans.add(ints);
                }
            }
        }
        return ans.toArray(new int[1][R*C]);
    }
}
